﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;

namespace 枸杞
{
    public static class Logger
    {
        public static bool 是否输出到控制台 { get; set; } = true;
        public static bool 是否输出错误 { get; set; } = true;
        public static bool 是否输出到文件 { get; set; } = true;
        public static bool 是否输出日志 { get; set; } = true;
        public static bool TraceLogEnabled { get; set; } = true;//net4.0不支持
        public static string? Logs文件夹路径 { get; set; }
        public static string 时间戳格式 { get; set; } = "HH:mm:ss.fff";
        private static string 日期时间戳格式 { get; set; } = "yyyy-MM-dd HH:mm:ss.fff";
        private static readonly BlockingCollection<string> 日志队列 = [];
        private static readonly Thread? 写入线程 = new(写日志)
        {
            Name = "Log writer thread",
            IsBackground = true,
            Priority = ThreadPriority.BelowNormal
        };
        private static StreamWriter? 当前StreamWriter;
        private static int 今天 = -1;

        //4.0不支持CallerMemberName等
        public static void Trace
        (
            string msg,
            [CallerMemberName]
            string memberName = "",
            [CallerFilePath]
            string sourceFilePath = "",
            [CallerLineNumber]
            int sourceLineNumber = 0
        )
        {
            if (!TraceLogEnabled) return;
            日志队列.Add(string.Format("{0} TRC {1}:{2}[{3}] {4}\r\n", DateTime.Now.ToString(时间戳格式),
                Path.GetFileName(sourceFilePath), memberName,
                sourceLineNumber, msg));
        }

        // INTERNALS ------------------------------------------------------------
        //static constructor
        static Logger()
        {
            //AppDomain.CurrentDomain.ProcessExit += (s, a) => 优雅地退出();

            写入线程.Start();
            日志队列.Add(string.Format("-------------------------------------------------\r\n" +
                                       "{0} 日志系统已初始化 \r\n" +
                                       "-------------------------------------------------\r\n",
                DateTime.Now.ToString(日期时间戳格式)));
        }

        private static StreamWriter? LogFileStreamWriter
        {
            get
            {
                if (DateTime.Now.Day == 今天) return 当前StreamWriter;
                今天 = DateTime.Now.Day;
                Logs文件夹路径 = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? "", "logs");
                if (!Directory.Exists(Logs文件夹路径)) Directory.CreateDirectory(Logs文件夹路径);
                string logFile = Path.Combine(Logs文件夹路径, DateTime.Now.ToString("yyMMdd.lo\\g"));
                当前StreamWriter?.Dispose();
                当前StreamWriter = new StreamWriter(File.Open(logFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite), System.Text.Encoding.UTF8) { AutoFlush = true };
                return 当前StreamWriter;
            }
        }

        /// <summary>
        ///     It's best to subscribe this method to 'exiting' events of your application.
        ///     It still tries to exit gracefully, but not all the cases is possible to cover
        ///     without application-specific exit events.
        /// </summary>
        public static void 优雅地退出Log()
        {
            try
            {
                日志队列.CompleteAdding();
                写入线程?.Join(5000);
            }
            finally { }
        }

        public static void Error(string msg, params object[] par)
        {
            if (!是否输出错误) return;
            日志队列.Add(
                string.Format(
                    "{0} ERR {1}\r\n", DateTime.Now.ToString(时间戳格式),
                // need to check because {} symbols might break Format call
                (par == null || par.Length == 0) ? msg : string.Format(msg, par)));
        }

        public static void Error(Exception? ex)
        {
            Error(ex?.ToString() ?? "");
        }

        public static void Info(string msg, params object[] par)
        {
            if (!是否输出日志) return;
            日志队列.Add(string.Format("{0} INF {1}\r\n", DateTime.Now.ToString(时间戳格式),
                // need to check because {} symbols might break Format call
                (par == null || par.Length == 0) ? msg : string.Format(msg, par)));
        }

        // Infinite logging thread
        private static void 写日志()
        {
            string msg;
            try
            {
                while (true)
                {
                    msg = 日志队列.Take();
                    if (是否输出到文件) LogFileStreamWriter?.Write(msg);
                    if (是否输出到控制台)
                    {
                        var oldColor = Console.ForegroundColor;
                        var type = msg.Substring(9, 3);
                        switch (type)
                        {
                            case "ERR":
                                Console.ForegroundColor = ConsoleColor.Red;
                                break;

                            case "INF":
                                Console.ForegroundColor = ConsoleColor.Cyan;
                                break;
                        }
                        Console.Write(msg);
                        Console.ForegroundColor = oldColor;
                    }
                }
            }
            catch (Exception)
            {
                msg = "---------------------------------------------------\r\n" +
                     $"{DateTime.Now.ToString(日期时间戳格式)} 日志系统正在关闭\r\n" +
                      "---------------------------------------------------\r\n";
                if (是否输出到文件) LogFileStreamWriter?.Write(msg);
                if (是否输出到控制台) Console.Write(msg);
            }
        }

        // returns appSetting value if available
        //private static void TryLoadSetting(string name, ref bool val)
        //{
        //    bool test;
        //    if (bool.TryParse(ConfigurationManager.AppSettings[name], out test))
        //        val = test;
        //}
        //private static void TryLoadSetting(string name, ref string val)
        //{
        //    if (!string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings[name]))
        //        val = ConfigurationManager.AppSettings[name];
        //}
        // Property handles daily roll-over files
    }
}